import 'dart:async';
import 'package:dk_flutter_base/dk_core.dart';
import 'package:dk_flutter_base/dk_ui.dart';

mixin HttpBloc on Bloc<PageEvent, PageState> {
  BuildContext _context;

  BaseHttpClient _httpClient;

  LogicPageConfig _logicPageConfig;

  LoadingDialog loadingDialog;

  List<StreamSubscription> streamSubscriptionList = List();

  void showLoadingDialog() {
    if (null == loadingDialog) {
      loadingDialog = LoadingDialog.create();
    }
    loadingDialog.show(_getBuildContext());
  }

  void stopLoadingDialog() {
    if (null == loadingDialog) {
      return;
    }
    loadingDialog.cancel();
  }

  ///PageWidget 界面使用的网络请求方法
  ///
  ///* httpOption:封装了Dio请求需要的所有参数，例如接口地址，请求参数等，参考 [HttpOption]
  ///* showSuccessMsg:网络请求成功后是否显示toast，true：当且仅当网络请求的业务成功（即code为0000）时生效，弹出msg;false:不弹出
  ///* widgetBuilderByData:获取到数据后切换widget构建函数，当且仅当网络请求的业务成功（即code为0000）时生效，回调参数为最终的业务数据（即code、msg、data中的data）
  ///* showFailMsg:网络请求成功后是否显示toast，true：http状态码大于200小于300、业务数据Bean（即包含业务code、data、msg）为null、业务Code为null或者小于等于0时生效，弹出msg;false:不弹出
  ///* handleErrorCode:网络请求成功后，业务code不合法时（即非0000）的处理操作，该回调函数参数为DefaultResponseData
  ///* withAppBarInDebug:和failCallback互斥，错误界面是否显示appBar提供返回按钮，true:错误界面将显示appBar;false，不显示
  ///* failCallback:和withAppBarInDebug互斥，网络请求失败的回调方法，失败包括http状态码小于200大于300、没有响应数据、没有业务code，只支持(DefaultHttpError error){}和(DefaultHttpError error, dynamic originalError){}，其中originalError的类型为DioError
  ///
  /// 使用案例
  /// ```
  ///  pageBloc.pageHttp(
  ///     httpOption: HttpOption.get("https://www.baidu.com",queryMap: {"param1":"1"}) ,
  ///     widgetBuilderByData: (dynamic data) {
  ///       return BusinessBean.fromJson(data);
  ///     },
  ///     showFailMsg: false,
  ///     handleErrorCode: (DefaultResponseData responseData) {},
  ///     showSuccessMsg: false,
  ///     withAppBarInDebug: true,
  ///     failCallback: (DefaultHttpError error, dynamic dioError) {});
  ///  ```
  void pageHttp({
    @required HttpOption httpOption,
    bool showSuccessMsg,
    @required WidgetBuilderByData widgetBuilderByData,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    bool withAppBarInDebug,
    Function failCallback,
  }) {
    showSuccessMsg ??= false;
    showFailMsg ??= false;
    withAppBarInDebug ??= true;
    mapPageHttpEvent(HttpPageEvent.simple(
      httpOption: httpOption,
      showSuccessMsg: showSuccessMsg,
      widgetBuilderByData: widgetBuilderByData,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      withAppBarInDebug: withAppBarInDebug,
      failCallback: failCallback,
    ));
  }

  ///参考[pageHttp]
  void pageHttpGet({
    @required String path,
    Map<String, dynamic> queryMap,
    bool showSuccessMsg,
    @required WidgetBuilderByData widgetBuilderByData,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    bool withAppBarInDebug,
    Function failCallback,
  }) {
    pageHttp(
      httpOption: HttpOption.get(path, queryMap: queryMap),
      widgetBuilderByData: widgetBuilderByData,
      showSuccessMsg: showSuccessMsg,
      handleErrorCode: handleErrorCode,
      showFailMsg: showFailMsg,
      withAppBarInDebug: withAppBarInDebug,
      failCallback: failCallback,
    );
  }

  ///参考[pageHttp]
  void pageHttpDelete({
    @required String path,
    Map<String, dynamic> queryMap,
    bool showSuccessMsg,
    @required WidgetBuilderByData widgetBuilderByData,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    bool withAppBarInDebug,
    Function failCallback,
  }) {
    pageHttp(
      httpOption: HttpOption.delete(path, queryMap: queryMap),
      showSuccessMsg: showSuccessMsg,
      widgetBuilderByData: widgetBuilderByData,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      withAppBarInDebug: withAppBarInDebug,
      failCallback: failCallback,
    );
  }

  ///参考[pageHttp]
  void pageHttpPost({
    @required String path,
    dynamic data,
    bool showSuccessMsg,
    @required WidgetBuilderByData widgetBuilderByData,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    bool withAppBarInDebug,
    Function failCallback,
  }) {
    pageHttp(
      httpOption: HttpOption.post(path, data: data),
      widgetBuilderByData: widgetBuilderByData,
      showSuccessMsg: showSuccessMsg,
      handleErrorCode: handleErrorCode,
      showFailMsg: showFailMsg,
      withAppBarInDebug: withAppBarInDebug,
      failCallback: failCallback,
    );
  }

  ///参考[pageHttp]
  void pageHttpPut({
    @required String path,
    dynamic data,
    bool showSuccessMsg,
    @required WidgetBuilderByData widgetBuilderByData,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    bool withAppBarInDebug,
    Function failCallback,
  }) {
    pageHttp(
      httpOption: HttpOption.put(path, data: data),
      widgetBuilderByData: widgetBuilderByData,
      showSuccessMsg: showSuccessMsg,
      handleErrorCode: handleErrorCode,
      showFailMsg: showFailMsg,
      withAppBarInDebug: withAppBarInDebug,
      failCallback: failCallback,
    );
  }

  ///PageWidget 界面在完成初始界面请求后，再次发起请求时使用的网络请求方法
  ///该方法包含LoadingDialog，如果showLoading设置为true则发起网络请求前显示LoadingDialog，结束后关闭LoadingDialog
  ///
  ///* httpOption:封装了Dio请求需要的所有参数，例如接口地址，请求参数等，参考 [HttpOption]
  ///* showLoading:发起请求时是否显示LoadingDialog，参考[LoadingDialog]，true：显示;false:不显示
  ///* showSuccessMsg:网络请求成功后是否显示toast，true：当且仅当网络请求的业务成功（即code为0000）时生效，弹出msg;false:不弹出
  ///* handleResult:当且仅当网络请求成功且业务成功时（即业务码为0000）的回调，该回调返回业务数据（即包含业务code、data、msg中的data）,格式参考[HandleResult]
  ///* showFailMsg:网络请求成功后是否显示toast，true：http状态码大于200小于300、业务数据Bean（即包含业务code、data、msg）为null、业务Code为null或者小于等于0时生效，弹出msg;false:不弹出
  ///* handleErrorCode:网络请求成功后，业务code不合法时（即非0000）的处理操作，该回调函数参数为DefaultResponseData
  ///* handleFailResult:网络请求失败的回调方法，不设置该方法网络失败展示默认界面,失败包括网络请求失败、http状态码小于200大于300、没有响应数据、没有业务code，只支持(DefaultHttpError error){}和(DefaultHttpError error, dynamic originalError){}，其中originalError的类型为DioError，,格式参考[HandleResult2]
  ///
  /// 使用案例
  /// ```
  /// pageBloc.dataHttp(
  ///     httpOption: HttpOption.get("https://www.baidu.com", queryMap: {"param1": "1"}),
  ///     showLoading: true,
  ///     handleResult: (dynamic data) {},
  ///     showSuccessMsg: false,
  ///     showFailMsg: false,
  ///     handleErrorCode: (DefaultResponseData responseData) {},
  ///     handleFailResult: (DefaultHttpError error, dynamic originalError) {},
  /// );
  ///  ```
  void dataHttp({
    @required HttpOption httpOption,
    bool showLoading,
    bool showSuccessMsg,
    HandleResult handleResult,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    HandleResult2 handleFailResult,
  }) {
    showLoading ??= true;
    showSuccessMsg ??= false;
    showFailMsg ??= false;
    mapDataHttpEvent(HttpDataEvent.simple(
      httpOption: httpOption,
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      handleResult: handleResult,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      handleFailResult: handleFailResult,
    ));
  }

  ///参考[dataHttp]
  void dataHttpGet({
    @required String path,
    Map<String, dynamic> queryMap,
    bool showLoading,
    bool showSuccessMsg,
    HandleResult handleResult,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    HandleResult2 handleFailResult,
  }) {
    dataHttp(
      httpOption: HttpOption.get(path, queryMap: queryMap),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      handleResult: handleResult,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      handleFailResult: handleFailResult,
    );
  }

  ///参考[dataHttp]
  void dataHttpDelete({
    @required String path,
    Map<String, dynamic> queryMap,
    bool showLoading,
    bool showSuccessMsg,
    HandleResult handleResult,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    HandleResult2 handleFailResult,
  }) {
    dataHttp(
      httpOption: HttpOption.delete(path, queryMap: queryMap),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      handleResult: handleResult,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      handleFailResult: handleFailResult,
    );
  }

  ///参考[dataHttp]
  void dataHttpPost({
    @required String path,
    dynamic data,
    bool showLoading,
    bool showSuccessMsg,
    HandleResult handleResult,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    HandleResult2 handleFailResult,
  }) {
    dataHttp(
      httpOption: HttpOption.post(path, data: data),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      handleResult: handleResult,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      handleFailResult: handleFailResult,
    );
  }

  ///参考[dataHttp]
  void dataHttpPut({
    @required String path,
    dynamic data,
    bool showLoading,
    bool showSuccessMsg,
    HandleResult handleResult,
    bool showFailMsg,
    HandleErrorCode handleErrorCode,
    HandleResult2 handleFailResult,
  }) {
    dataHttp(
      httpOption: HttpOption.put(path, data: data),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      handleResult: handleResult,
      showFailMsg: showFailMsg,
      handleErrorCode: handleErrorCode,
      handleFailResult: handleFailResult,
    );
  }

  ///PageWidget界面界面中的子在widget发起请求时使用的网络请求方法，该子widget必须时[BaseWidget]或者其子类
  ///该方法的使用通常情况下是将PageWidget中的pageBloc传递给子widget，子需widget利用pageWidget的pageBloc发起网络请求，成功（网络和业务）后利用子widget提供的[WidgetBloc]子widget的视图,
  ///该方法默认将错误业务码包装成[WidgetHandleErrorCodeEvent]，通过子widget的bloc发送给子widget
  ///该方法默认将结果数据装成[WidgetHandleResultEvent]，通过子widget的bloc发送给子widget
  ///该方法默认将网络错误数据装成[WidgetLogicEvent]，通过子widget的bloc发送给子widget，显示错误界面，点击重试按钮重新发起请求
  ///
  ///* widgetBloc:子widget中的bloc,参考[WidgetBloc]，主要用于在网络请求成功且业务成功后切换子widget的视图
  ///* httpOption:封装了Dio请求需要的所有参数，例如接口地址，请求参数等，参考 [HttpOption]
  ///* showLoading:发起请求时是否显示LoadingDialog，参考[LoadingDialog]，true：显示;false:不显示
  ///* showSuccessMsg:网络请求成功后是否显示toast，true：当且仅当网络请求的业务成功（即code为0000）时生效，弹出msg;false:不弹出
  ///* showFailMsg:网络请求成功后是否显示toast，true：http状态码大于200小于300、业务数据Bean（即包含业务code、data、msg）为null、业务Code为null或者小于等于0时生效，弹出msg;false:不弹出
  ///* handleFailResult:网络请求失败的回调方法，不设置该方法网络失败展示默认界面,失败包括网络请求失败、http状态码小于200大于300、没有响应数据、没有业务code，只支持(WidgetBloc widgetBloc, DefaultHttpError error, dynamic originalError){}，其中originalError的类型为DioError，,格式参考[HandleResult4BaseWidget]
  ///
  /// 使用案例
  /// ```
  /// pageBloc.widgetHttp(
  ///      widgetBloc: widgetBloc,
  ///      httpOption:
  ///      HttpOption.get("https://www.baidu.com", queryMap: {"param1": "1"}),
  ///      showLoading: false,
  ///      showSuccessMsg: false,
  ///      showFailMsg: false,
  ///      handleFailResult: (DefaultHttpError error, dynamic originalError) {},
  /// );
  ///  ```
  void widgetHttp({
    @required WidgetBloc widgetBloc,
    @required HttpOption httpOption,
    bool showLoading,
    bool showSuccessMsg,
    bool showFailMsg,
    HandleResult4BaseWidget handleFailResult,
  }) {
    showLoading ??= true;
    showSuccessMsg ??= false;
    showFailMsg ??= false;
    mapWidgetHttpEvent(HttpWidgetEvent.simple(
      widgetBloc: widgetBloc,
      httpOption: httpOption,
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      showFailMsg: showFailMsg,
      handleFailResult: handleFailResult,
    ));
  }

  ///参考[widgetHttp]
  void widgetHttpGet({
    WidgetBloc widgetBloc,
    String path,
    Map<String, dynamic> queryMap,
    bool showLoading,
    bool showSuccessMsg,
    bool showFailMsg,
    HandleResult4BaseWidget handleFailResult,
  }) {
    widgetHttp(
      widgetBloc: widgetBloc,
      httpOption: HttpOption.get(path, queryMap: queryMap),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      showFailMsg: showFailMsg,
      handleFailResult: handleFailResult,
    );
  }

  ///参考[widgetHttp]
  void widgetHttpDelete({
    WidgetBloc widgetBloc,
    String path,
    Map<String, dynamic> queryMap,
    bool showLoading,
    bool showSuccessMsg,
    bool showFailMsg,
    HandleResult4BaseWidget handleFailResult,
  }) {
    widgetHttp(
      widgetBloc: widgetBloc,
      httpOption: HttpOption.delete(path, queryMap: queryMap),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      showFailMsg: showFailMsg,
      handleFailResult: handleFailResult,
    );
  }

  ///参考[widgetHttp]
  void widgetHttpPost({
    WidgetBloc widgetBloc,
    String path,
    dynamic data,
    bool showLoading,
    bool showSuccessMsg,
    bool showFailMsg,
    Function failCallback,
    HandleResult4BaseWidget handleFailResult,
  }) {
    widgetHttp(
      widgetBloc: widgetBloc,
      httpOption: HttpOption.post(path, data: data),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      showFailMsg: showFailMsg,
      handleFailResult: handleFailResult,
    );
  }

  ///参考[widgetHttp]
  void widgetHttpPut({
    WidgetBloc widgetBloc,
    String path,
    dynamic data,
    bool showLoading,
    bool showSuccessMsg,
    bool showFailMsg,
    HandleResult4BaseWidget handleFailResult,
  }) {
    widgetHttp(
      widgetBloc: widgetBloc,
      httpOption: HttpOption.put(path, data: data),
      showLoading: showLoading,
      showSuccessMsg: showSuccessMsg,
      showFailMsg: showFailMsg,
      handleFailResult: handleFailResult,
    );
  }

  BuildContext getBuildContext();

  BuildContext _getBuildContext() {
    return _context ??= getBuildContext();
  }

  BaseHttpClient getHttpClient();

  BaseHttpClient _getHttpClient() {
    return _httpClient ??= getHttpClient();
  }

  LogicPageConfig getLogicPageConfig(String path);

  LogicPageConfig _getLogicPageConfig(String path) {
    return _logicPageConfig ??= getLogicPageConfig(path);
  }

  ///[pageHttp]函数真正执行的逻辑，提供默认处理
  void mapPageHttpEvent(HttpPageEvent event) {
    streamSubscriptionList.add(_getHttpClient()?.request(
      httOption: event?.httpOption,
      successCallback: event?.successCallback ??
          handleNetSuccess4Page(
            switchPage: (dynamic data) {
              dispatch(
                PageSwitchEvent(
                  widgetBuilder: (BuildContext context) {
                    if (objEmpty(data)) {
                      return LogicPage.emptyPage(
                          emptyParam:
                              _getLogicPageConfig(event?.httpOption?.path)
                                  ?.empty);
                    }
                    return objEmpty(event?.widgetBuilderByData)
                        ? Text("请创建Widget")
                        : event?.widgetBuilderByData(data);
                  },
                ),
              );
            },
            showMsg: event?.showSuccessMsg,
            handleErrorCode: event?.handleErrorCode,
          ),
      failCallback: event?.failCallback ??
          handleNetFail4Page(
            showMsg: event?.showFailMsg,
            withAppBarInDebug: event?.withAppBarInDebug,
            errorParam: _getLogicPageConfig(event?.httpOption?.path).error
              ..listener = () {
                mapPageHttpEvent(event);
              },
            retryListener: () {
              mapPageHttpEvent(event);
            },
            switchPage: (Widget widget) {
              dispatch(
                PageSwitchEvent(
                  widgetBuilder: (BuildContext context) {
                    return widget;
                  },
                ),
              );
            },
          ),
    ));
  }

  ///[dataHttp]函数真正执行的逻辑，提供默认处理
  void mapDataHttpEvent(HttpDataEvent event) {
    if (checkBool(event?.showLoading)) {
      showLoadingDialog();
    }
    streamSubscriptionList.add(
      _getHttpClient()?.request(
        httOption: event?.httpOption,
        successCallback: event?.successCallback ??
            handleNetSuccess4Data(
              showMsg: event?.showSuccessMsg,
              onRequestFinish: checkBool(event?.showLoading)
                  ? () {
                      stopLoadingDialog();
                    }
                  : null,
              handleErrorCode: event?.handleErrorCode,
              handleResult: event?.handleResult,
            ),
        failCallback: event?.failCallback ??
            handleNetFail4Data(
              onRequestFinish: checkBool(event?.showLoading)
                  ? () {
                      stopLoadingDialog();
                    }
                  : null,
              showMsg: event?.showFailMsg,
              handleResult: event?.handleFailResult ??
                  handleFailData(
                    retryListener: () {
                      mapDataHttpEvent(event);
                    },
                    switchPage: (Widget widget) {
                      dispatch(
                        PageSwitchEvent(
                          widgetBuilder: (BuildContext context) {
                            return widget;
                          },
                        ),
                      );
                    },
                  ),
            ),
      ),
    );
  }

  ///[widgetHttp]函数真正执行的逻辑，提供默认处理
  void mapWidgetHttpEvent(HttpWidgetEvent event) {
    if (checkBool(event?.showLoading)) {
      showLoadingDialog();
    }
    streamSubscriptionList.add(
      _getHttpClient()?.request(
        httOption: event?.httpOption,
        successCallback: event?.successCallback ??
            handleNetSuccess4Data(
              showMsg: event?.showSuccessMsg,
              onRequestFinish: checkBool(event?.showLoading)
                  ? () {
                      stopLoadingDialog();
                    }
                  : null,
              handleErrorCode: (DefaultResponseData responseData) {
                event?.widgetBloc?.dispatch(
                    WidgetHandleErrorCodeEvent(responseData: responseData));
              },
              handleResult: (dynamic result) {
                event?.widgetBloc
                    ?.dispatch(WidgetHandleResultEvent(result: result));
              },
            ),
        failCallback: event?.failCallback ??
            handleNetFail4Widget(
              widgetBloc: event?.widgetBloc,
              onRequestFinish: checkBool(event?.showLoading)
                  ? () {
                      stopLoadingDialog();
                    }
                  : null,
              showMsg: event?.showFailMsg,
              handleResult: event?.handleFailResult ??
                  (WidgetBloc widgetBloc, DefaultHttpError error, dynamic originalError) {
                    widgetBloc?.dispatch(
                      WidgetLogicEvent(
                        logicParam:
                            _getLogicPageConfig(event.httpOption.path).error
                              ..listener = () {
                                mapWidgetHttpEvent(event);
                              },
                      ),
                    );
                  },
            ),
      ),
    );
  }

  ///停止所有当前进行的网络请求
  void cancelAllHttp() {
    streamSubscriptionList?.forEach((StreamSubscription subscription) {
      subscription?.cancel();
    });
  }

  ///异步方式停止所有当前进行的网络请求
  Future cancelAllHttpAsync() async {
    if (listEmpty(streamSubscriptionList)) {
      return Future.value();
    }
    return Future.forEach(streamSubscriptionList,
        (StreamSubscription subscription) {
      return subscription?.cancel();
    });
  }

  @override
  void dispose() {
    super.dispose();
    loadingDialog?.cancel();
    loadingDialog = null;
    cancelAllHttp();
    streamSubscriptionList.clear();
  }
}

///page请求数据
class HttpPageEvent {
  HttpOption httpOption;

  Function successCallback;
  Function failCallback;

  HttpPageEvent.normal(
      {@required this.httpOption, this.successCallback, this.failCallback})
      : widgetBuilderByData = null,
        showSuccessMsg = null,
        handleErrorCode = null,
        showFailMsg = null,
        withAppBarInDebug = null;

  WidgetBuilderByData widgetBuilderByData;
  bool showSuccessMsg;
  HandleErrorCode handleErrorCode;
  bool showFailMsg;
  bool withAppBarInDebug;

  HttpPageEvent.simple({
    @required this.httpOption,
    this.widgetBuilderByData,
    this.showSuccessMsg = false,
    this.handleErrorCode,
    this.showFailMsg = false,
    this.withAppBarInDebug = true,
    this.failCallback,
  }) : successCallback = null;
}

///第二次请求数据
class HttpDataEvent {
  HttpOption httpOption;

  Function successCallback;
  Function failCallback;

  HttpDataEvent.normal(
      {@required this.httpOption, this.successCallback, this.failCallback})
      : showLoading = null,
        handleResult = null,
        showSuccessMsg = null,
        handleErrorCode = null,
        showFailMsg = null,
        handleFailResult = null;

  bool showLoading;
  HandleResult handleResult;
  bool showSuccessMsg;
  HandleErrorCode handleErrorCode;
  bool showFailMsg;
  HandleResult2 handleFailResult;

  HttpDataEvent.simple({
    @required this.httpOption,
    this.showLoading = true,
    this.handleResult,
    this.showSuccessMsg = false,
    this.handleErrorCode,
    this.showFailMsg = false,
    this.handleFailResult,
  })  : successCallback = null,
        failCallback = null;
}

///为子widget请求数据
class HttpWidgetEvent {
  HttpOption httpOption;

  Function successCallback;
  Function failCallback;

  HttpWidgetEvent.normal({
    @required this.httpOption,
    this.successCallback,
    this.failCallback,
  })  : showLoading = null,
        showSuccessMsg = null,
        showFailMsg = null,
        widgetBloc = null,
        handleFailResult = null;

  bool showLoading;
  bool showSuccessMsg;
  bool showFailMsg;
  WidgetBloc widgetBloc;
  HandleResult4BaseWidget handleFailResult;

  HttpWidgetEvent.simple({
    @required this.widgetBloc,
    @required this.httpOption,
    this.showLoading = true,
    this.showSuccessMsg = false,
    this.showFailMsg = false,
    this.handleFailResult,
  })  : successCallback = null,
        failCallback = null;
}
